Homography Matrix 单应矩阵

What is Homography

单应(Homography)是射影几何中的概念,又称为射影变换。它把一个射影平面上的点(三维齐次矢量)映射到另一个射影平面上,并且把直线映射为直线,具有保线性质。总的来说,单应是关于三维齐次矢量的一种线性变换,可以用一个3×3的非奇异矩阵H表示

Consider two images of a plane (top of the book) shown in Figure 1. The red dot represents the same physical point in the two images. In computer vision jargon we call these corresponding points. Figure 1. shows four corresponding points in four different colors — red, green, yellow and orange. A Homography is a transformation ( a 3×3 matrix ) that maps the points in one image to the corresponding points in the other image.

这是一个齐次坐标的等式,H乘以一个非零的比例因子上述等式仍然成立,即H是一个3×3齐次矩阵,具有8个未知量.

单应矩阵求解

将矩阵的乘法展开,即可得到

方便求解,可以将上面等式变换为Ax=b的形式,做如下变换,第一和第二个式子的左右两边同时乘以第三个式子的左右两边得到

将式子的右边变为0

将上面的等式改写为向量积的形式,令

单应矩阵H是一个齐次矩阵,可以将其最后一个元素归一化为1。

一对匹配的点对,可以得到上述等式,H有8个未知量,也就说最少4对匹配的点对(任意3点不共线),就可以求出两幅图像的单应矩阵H。但是通常来说,图像的匹配点对要超过4对,设得到了n对匹配的点对,可以得到如下的等式

对于大于4对点的情况可以使用线性最小二乘求解。

一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
******************************************************************************
* @file
* @author maky <chengwei920412@outlook.com>
* @version
* @date 2018-01-04 16:22:58
* @brief
******************************************************************************
* @attention
*
*
******************************************************************************
*/
#include <iostream>
#include <Eigen/Eigen>
#include <Eigen/geometry>
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/core/eigen.hpp>

cv::Mat findHomography(const std::vector<cv::Point2f> &xs1, const std::vector<cv::Point2f> &xs2)
{
if (xs1.size() != xs2.size()) {
return cv::Mat();
}
int size = xs1.size();
Eigen::Matrix<double, Eigen::Dynamic, 9> A = Eigen::MatrixXd::Zero(2 * size, 9);
for (auto pos = 0; pos < size; pos++) {
auto &x1 = xs1.at(pos);
auto &x2 = xs2.at(pos);
auto rowx = pos * 2;
auto rowy = rowx + 1;
A(rowx, 0) = x1.x;
A(rowx, 1) = x1.y;
A(rowx, 2) = 1;
A(rowx, 6) = -1 * x1.x * x2.x;
A(rowx, 7) = -1 * x1.y * x2.x;

A(rowy, 3) = x1.x;
A(rowy, 4) = x1.y;
A(rowy, 5) = 1;
A(rowy, 6) = -1 * x1.x * x2.y;
A(rowy, 7) = -1 * x1.y * x2.y;

A(rowx, 8) = -1 * x2.x;
A(rowy, 8) = -1 * x2.y;
}
Eigen::JacobiSVD<Eigen::Matrix<double, Eigen::Dynamic, 9>> solver(A, Eigen::ComputeFullV);
const Eigen::VectorXd x = solver.matrixV().rightCols(1);
Eigen::Matrix3d h;
h << x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8];
h.normalize();
cv::Mat homography;
cv::eigen2cv(h, homography);
return homography;
}

int main(int argc, const char **argv)
{
cv::Mat source = cv::imread("1546583988066087246-1.png");
cv::Mat patch = cv::imread("patch.png");
auto dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::Ptr<cv::aruco::DetectorParameters> parameters = cv::aruco::DetectorParameters::create();
// detect
std::vector<int> ids;
std::vector<std::vector<cv::Point2f>> corners, rejected_candidates;
cv::aruco::detectMarkers(source, dictionary, corners, ids, parameters, rejected_candidates);

std::vector<cv::Point2f> patch_points;
patch_points.push_back(cv::Point2f(0., 0.));
patch_points.push_back(cv::Point2f(patch.cols - 1, 0.));
patch_points.push_back(cv::Point2f(patch.cols - 1, patch.rows - 1));
patch_points.push_back(cv::Point2f(0., patch.rows - 1));

//cv::Mat homography = cv::findHomography(patch_points, corners[0]);
cv::Mat homography = findHomography(patch_points, corners[0]);
cv::Mat output;
cv::warpPerspective(patch, output, homography, source.size());

for (auto u = 0; u < source.cols; u++) {
for (auto v = 0; v < source.rows; v++) {
auto pixel = output.at<cv::Vec3b>(v, u);
if (pixel[0] != 0 || pixel[1] != 0 || pixel[2] != 0) {
source.at<cv::Vec3b>(v, u) = pixel;
}
}
}
cv::imwrite("patched.png", source);
cv::imshow("source", source);
cv::imshow("patch", patch);
cv::imshow("output", output);

while (true) {
auto key = cv::waitKey(1);
if (' ' == key) {
break;
}
}
return 0;
}


Reference

https://www.learnopencv.com/homography-examples-using-opencv-python-c/

https://www.cnblogs.com/wangguchangqing/p/8287585.html

https://docs.opencv.org/3.4/d9/dab/tutorial_homography.html

http://www.cse.psu.edu/~rtc12/CSE486/lecture16.pdf

https://ags.cs.uni-kl.de/fileadmin/inf_ags/3dcv-ws11-12/3DCV_WS11-12_lec04.pdf